#include "Common.h"

#include "Queue/App_CycleQueue.h"

/**********************************************************
 * Function Name:        AppCycleQueue_Create
 * Description:          alloc memory for queue
 * Parameters[in]:       IN UINT16 u16ItemSize
                         IN UINT16 u16Queuesize
                         INOUT APP_CYCLE_QUEUE_s **ppstQ
 * Parameters[out]:      INOUT APP_CYCLE_QUEUE_s **ppstQ
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.26
***********************************************************/
ULONG AppCycleQueue_Create(IN UINT16 u16ItemSize, IN UINT16 u16Queuesize, INOUT APP_CYCLE_QUEUE_s *pstQ)
{
    if ((u16ItemSize <= 0) || (u16Queuesize <= 0) || (pstQ == NULL))
    {
        return STAT_ERR_INVAILD_PARAM;
    }
    
	pstQ->pu8Base = (UINT8 *)calloc(sizeof(UINT8), u16ItemSize * u16Queuesize);
	if (pstQ->pu8Base == NULL)
	{
        pstQ = NULL;
		return STAT_ERR_CALLOC;
	}
	
	pstQ->u16Front = 0;         
	pstQ->u16Rear = 0;
	pstQ->u16Queuesize = u16Queuesize;
    pstQ->u16ItemSize = u16ItemSize;
	
    pthread_rwlock_init(&pstQ->stMtx, NULL);

	return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppCycleQueue_IsFull
 * Description:          check if queue full
 * Parameters[in]:       IN APP_CYCLE_QUEUE_s *Q
 * Parameters[out]:      void
 * ReturnValue:          BOOL
 * Author:               liuke
 * Date:                 2020.05.26
***********************************************************/
BOOL AppCycleQueue_IsFull(IN APP_CYCLE_QUEUE_s *pstQ)
{
	if(pstQ->u16Front == ((pstQ->u16Rear + 1) % pstQ->u16Queuesize))
		return BOOL_TRUE;
	else
		return BOOL_FALSE;
}

/**********************************************************
 * Function Name:        AppCycleQueue_IsEmpty
 * Description:          check if queue empty
 * Parameters[in]:       IN APP_CYCLE_QUEUE_s * Q
 * Parameters[out]:      void
 * ReturnValue:          BOOL
 * Author:               liuke
 * Date:                 2020.05.26
***********************************************************/
BOOL AppCycleQueue_IsEmpty(IN APP_CYCLE_QUEUE_s *pstQ)
{
	if(pstQ->u16Front == pstQ->u16Rear)
		return BOOL_TRUE;
	else
		return BOOL_FALSE;
}

/**********************************************************
 * Function Name:        AppCycleQueue_Enqueue
 * Description:          pop element from queue
 * Parameters[in]:       IN APP_CYCLE_QUEUE_s * Q
                         IN UINT8 *pu8Val
                         IN UINT16 u16Len
 * Parameters[out]:      void
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.26
***********************************************************/
ULONG AppCycleQueue_Enqueue(IN APP_CYCLE_QUEUE_s *pstQ, IN UINT8 *pu8Val, IN UINT16 u16Len)
{ 
    if ((pstQ == NULL) || (pu8Val == NULL) || (u16Len > pstQ->u16ItemSize))
    {
        return  STAT_ERR_INVAILD_PARAM;   
    }

    pthread_rwlock_wrlock(&pstQ->stMtx);

	if(AppCycleQueue_IsFull(pstQ))
	{
        pthread_rwlock_unlock(&pstQ->stMtx);
		return STAT_ERR_QUEUE_FULL;
	}

    if (u16Len > pstQ->u16ItemSize)
    {
        LOG_WARN("Warning Msg will truncation: len=%d, itemsize %d\n", u16Len, pstQ->u16ItemSize);
        pthread_rwlock_unlock(&pstQ->stMtx);
        return STAT_ERR_QUEUE_PUT_SIZE;
    }

    memcpy(pstQ->pu8Base + pstQ->u16Rear * pstQ->u16ItemSize, pu8Val, u16Len);
    pstQ->u16Rear = (pstQ->u16Rear + 1) % pstQ->u16Queuesize;

    pthread_rwlock_unlock(&pstQ->stMtx);

	return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppCycleQueue_Dequeue
 * Description:          push into queue
 * Parameters[in]:       IN APP_CYCLE_QUEUE_s * Q
                         INOUT UINT8 *pu8Val
                         IN UINT16 u16Len
 * Parameters[out]:      INOUT UINT8 *pu8Val
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2020.05.26
***********************************************************/
ULONG AppCycleQueue_Dequeue(IN APP_CYCLE_QUEUE_s *pstQ, INOUT UINT8 *pu8Val, IN UINT16 u16Len)
{
    if ((pstQ == NULL) || (pu8Val == NULL))
    {
        return  STAT_ERR_INVAILD_PARAM;   
    }

    pthread_rwlock_rdlock(&pstQ->stMtx);

    if(AppCycleQueue_IsEmpty(pstQ))
    {
        pthread_rwlock_unlock(&pstQ->stMtx);
        return STAT_ERR_QUEUE_EMPTY;
    }

    if (u16Len < pstQ->u16ItemSize)
    {
        LOG_WARN("Warning Msg will truncation: len=%d, itemsize %d\n", u16Len, pstQ->u16ItemSize);
        pthread_rwlock_unlock(&pstQ->stMtx);
        return STAT_ERR_QUEUE_GET_SIZE;
    }

    memcpy(pu8Val, pstQ->pu8Base + pstQ->u16Front * pstQ->u16ItemSize, pstQ->u16ItemSize);
    pstQ->u16Front = (pstQ->u16Front + 1) % pstQ->u16Queuesize;

	pthread_rwlock_unlock(&pstQ->stMtx);

	return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppCycleQueue_Destory
 * Description:          delete queue
 * Parameters[in]:       INOUT APP_CYCLE_QUEUE_s **ppstQ
 * Parameters[out]:      INOUT APP_CYCLE_QUEUE_s **ppstQ
 * ReturnValue:          void
 * Author:               liuke
 * Date:                 2020.05.26
***********************************************************/
void AppCycleQueue_Destory(INOUT APP_CYCLE_QUEUE_s *pstQ)
{
    pthread_rwlock_destroy(&pstQ->stMtx);

    free(pstQ->pu8Base);
    pstQ->pu8Base = NULL;

    return;
}
